/**
 * 移动端 picker 插件
 * @author zyl
 * @date 2018-12-30
 */

; (function ($) {
  'use strict';

  var modalHelper = (function (bodyCls) {
    var scrollTop;
    return {
      afterOpen: function () {
        scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
        document.body.classList.add(bodyCls);
        document.body.style.top = -scrollTop + 'px';
      },
      beforeClose: function () {
        document.body.classList.remove(bodyCls);
        try {
          document.scrollingElement.scrollTop = scrollTop;
        } catch (e) {
          document.documentElement.scrollTop = screenTop;
          document.body.scrollTop = scrollTop;
        }
      }
    };
  })('picker-open');

  var $body = $(document.body);
  var $parent;
  var $main;
  var $cancel;
  var $title;
  var $ok;
  var $slot;
  var $ul;
  var $wrap;

  var parentFontSize = 14; // 最外层元素的 font-size(px)
  var translateY; // 列表在 Y 轴的移动距离
  var touchStartY; // 开始触摸时的 Y 坐标
  var touchMoveY; // 手指移动时的实时 Y 坐标
  var oldTouchY; // 手指移动时上一次的 y 坐标
  var maxTranslateY; // 沿 Y 轴最大移动距离(em)
  var minTranslateY; // 沿 Y 轴最小移动距离(em)
  var touchStartTime; // 开始触摸时的时间
  var touchMoveTime; // 手指移动时的实时时间
  var slowMovingTimer; // 松手时，执行缓动效果的定时器
  var mv; // 手指移动的速度
  var rows = 7; // 选项列表行数为 7
  var rowH = 2.5; // 选项列表每行的行高(em)
  var selectedIndex; // 选中项的索引
  var selectedValue; // 选中项的 value 值
  var self;

  var defaultOpts = {
    title: '标题',
    okText: '确定',
    cancelText: '取消',
    data: [], // 数据源 {*}
    name: '', // 取数据源中的哪一个字段作为展示字段 {*}
    value: '', // 取数据源每一项中的哪一个字段作为实际的 val {*}
    selectedIndex: 0, // 数据源中的第几条数据默认被选中
    onOk: null, // 点击确定时执行的回调 
    onCancel: null, // 点击取消时执行的回调
    pickerId: '', // picker 组件的唯一标识 {*}
  };
  var options;

  var Picker = function (opts) {
    self = this;
    options = opts;
    selectedIndex = opts.selectedIndex;
    selectedValue = options.data[selectedIndex][options.value];

    if (!$body.find('#' + options.pickerId).size()) {
      $parent = $($.fn.picker.template.whole).attr('id', options.pickerId);
      $body.append($parent.html($.fn.picker.template.main));
      self.getDOM().initHead().initItems().initPos().initTouch();
    }
  };

  Picker.prototype = {
    constructor: Picker,

    // GET DOM
    getDOM: function () {
      $main = findDOM('.main');
      $cancel = findDOM('.btn-cancel');
      $title = findDOM('.title');
      $ok = findDOM('.btn-ok');
      $slot = findDOM('.slot');
      $ul = findDOM('#ul');
      $wrap = findDOM('.wrap');
      return self;
    },

    // 初始化 head
    initHead: function () {
      $slot.css('height', (rows * rowH) + 'em');
      $title.text(options.title);

      $cancel.text(options.cancelText);
      $cancel.on('click.picker.cancel', function (e) {
        e.preventDefault();
        clearInterval(slowMovingTimer);
        if (typeof options.onCancel === 'function') {
          options.onCancel();
        }
        self.hide();
      });

      $ok.text(options.okText);
      $ok.on('click.picker.ok', function (e) {
        e.preventDefault();
        clearInterval(slowMovingTimer);
        translateY = Math.round(translateY / rowH) * rowH;
        if (typeof options.onOk === 'function') {
          selectedIndex = selectedIndex - translateY / rowH;
          selectedValue = options.data[selectedIndex][options.value];
          options.onOk(selectedValue, selectedIndex);
        }
        self.hide();
      });
      return self;
    },

    // 初始化选项
    initItems: function () {
      var items = '';
      options.data.forEach(function (item) {
        items += '<div class="item" data-value="' + item[options.value] + '" style="height:' + rowH + 'em;line-height:' + rowH + 'em;">' + item[options.name] + '</div>';
      });
      $ul.append(items);
      return self;
    },

    // 初始化定位
    initPos: function () {
      var n = (rows - 1) / 2;
      var top = (n - selectedIndex) * rowH;
      translateY = 0;
      maxTranslateY = selectedIndex * rowH;
      minTranslateY = -(options.data.length - selectedIndex - 1) * rowH;
      $ul.css({ top: top + 'em', transform: 'translateY(' + translateY + ')' });
      return self;
    },

    // 初始化滑动
    initTouch: function () {
      $wrap.on('touchstart.picker', touchStart);
      $wrap.on('touchmove.picker', touchMove);
      $wrap.on('touchend.picker', touchEnd);

      // 开始滑动
      function touchStart(e) {
        e.preventDefault();
        clearInterval(slowMovingTimer);

        touchStartTime = new Date().getTime();
        touchMoveTime = touchStartTime;

        touchStartY = e.originalEvent.targetTouches[0].screenY;
        touchMoveY = touchStartY;
        oldTouchY = touchStartY;
      }

      // 滑动中
      function touchMove(e) {
        e.preventDefault();

        touchMoveTime = new Date().getTime();

        touchMoveY = e.originalEvent.targetTouches[0].screenY;
        var diffY = (touchMoveY - oldTouchY) / parentFontSize;
        translateY = translateY + diffY;
        oldTouchY = touchMoveY;

        $ul.css('transform', 'translateY(' + translateY + 'em)');

        if (e.originalEvent.targetTouches[0].screenY < 1) {
          touchEnd(e);
        }
      }

      // 滑动结束
      function touchEnd(e) {
        e.preventDefault();
        if (touchMoveTime === touchStartTime) {
          mv = 0;
        }
        else {
          mv = (touchMoveY - touchStartY) / (touchMoveTime - touchStartTime);
          if (Math.abs(mv) <= 0.2) { // 速度 <= 0.2
            mv = mv < 0 ? -0.08 : 0.08;
          }
          else {
            if (Math.abs(mv) <= 0.5) { // 0.2 < 速度 <= 0.5
              mv = mv < 0 ? -0.16 : 0.16;
            }
            else { // 速度 > 0.5
              mv = mv / 2;
            }
          }
        }
        slowMoving();
      }

      // 缓动
      function slowMoving() {
        clearInterval(slowMovingTimer);
        var d = 0;
        var curTranslateY;
        slowMovingTimer = setInterval(function () {
          // Math.exp(-0.03 * d ++)： 1 除以 e 的 0.03 * d 次幂，速度减缓曲线
          var curve = mv * Math.exp(-0.03 * d++);
          translateY = translateY + curve;

          // 当速度衰减到 0 ～ 0.1 时，计算的移动距离保持不变，并且是 rowH 的整数倍
          if (Math.abs(curve) < 0.1) {
            translateY = Math.round(translateY / rowH) * rowH;
          }

          if (translateY > 0 && translateY > maxTranslateY) {
            translateY = maxTranslateY;
          }

          if (translateY < 0 && translateY < minTranslateY) {
            translateY = minTranslateY;
          }

          // 当不再移动时，清除定时器
          if (curTranslateY === translateY) {
            clearInterval(slowMovingTimer);
            return;
          }

          curTranslateY = translateY;
          $ul.css('transform', 'translateY(' + translateY + 'em)');
        }, 20);
      }
    },

    // 显示
    show: function () {
      var e = $.Event('picker.show');
      $parent.removeClass('hide');
      $main[0].offsetWidth; // 手动触发 DOM 重排，为了实现过度动画效果
      $main.trigger(e).addClass('in');
      modalHelper.afterOpen();
      self.initPos();
    },

    // 隐藏
    hide: function () {
      var e = $.Event('picker.hide');
      $main.one('transitionend, webkitTransitionEnd', function () {
        modalHelper.beforeClose();
        $parent.addClass('hide');
      });
      $main.trigger(e).removeClass('in');
    },
  };

  $.fn.picker = function (opts) {
    var _this = this;
    _this.picker = new Picker($.extend({}, defaultOpts, opts));
    _this.on('click.picker.show', function (e) {
      e.preventDefault();
      _this.picker.show();
    });
    return _this;
  };

  $.fn.picker.template = {
    whole: '<div class="zyl-picker hide"></div>',
    main: '<div class="main">' +
      '<div class="head">' +
      '<div class="btn btn-cancel"></div>' +
      '<div class="title"></div>' +
      '<div class="btn btn-ok"></div>' +
      '</div>' +
      '<div class="slot">' +
      '<div id="ul"></div>' +
      '<div class="shadow shadow1"></div>' +
      '<div class="shadow shadow2"></div>' +
      '<div class="wrap"></div>' +
      '</div>' +
      '</div>'
  };

  function findDOM(selector) {
    return $parent.find(selector);
  }
}(jQuery));